package net.fltx.taskmgr.service;


import com.sun.jna.ptr.IntByReference;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.control.Alert;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import net.fltx.icon.IconUtil;
import net.fltx.taskmgr.entity.ProcessEntity;
import net.fltx.taskmgr.win32api.*;
import net.sf.image4j.codec.ico.ICODecoder;
import sun.applet.Main;
import org.deletethis.mejico.awt.IconParser;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.image.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Windows进程管理服务
 */
public class WindowsProcessService {
    public static final Map<Integer, ProcessEntity> procMap = new HashMap<>();
    public static final Map<String, Image> exeIconsCache = new HashMap<>();

    static {
        try {
            Image img = new Image(WindowsProcessService.class.getClassLoader().getResourceAsStream("unknown.png"));
            exeIconsCache.put("", img);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //进程列表
    private TreeTableView ttv_prc_list;
    private TreeTableColumn<ProcessEntity, String> ttc_pid;
    private TreeTableColumn<ProcessEntity, String> ttc_name;
    private TreeTableColumn<ProcessEntity, String> ttc_cpu;
    private TreeTableColumn<ProcessEntity, String> ttc_memory;
    private TreeTableColumn<ProcessEntity, String> ttc_path;

    public WindowsProcessService(TreeTableView ttv_prc_list, TreeTableColumn<ProcessEntity, String> ttc_pid, TreeTableColumn<ProcessEntity, String> ttc_name, TreeTableColumn<ProcessEntity, String> ttc_cpu, TreeTableColumn<ProcessEntity, String> ttc_memory, TreeTableColumn<ProcessEntity, String> ttc_path) {
        this.ttv_prc_list = ttv_prc_list;
        this.ttc_pid = ttc_pid;
        this.ttc_name = ttc_name;
        this.ttc_cpu = ttc_cpu;
        this.ttc_memory = ttc_memory;
        this.ttc_path = ttc_path;
    }

    public void init() {


        ttc_pid.setCellValueFactory((TreeTableColumn.CellDataFeatures<ProcessEntity, String> param) ->
                new ReadOnlyStringWrapper(String.valueOf(param.getValue().getValue().getPid())));
        ttc_name.setCellValueFactory((TreeTableColumn.CellDataFeatures<ProcessEntity, String> param) -> {
            return new ReadOnlyStringWrapper(param.getValue().getValue().getName());
        });
        ttc_cpu.setCellValueFactory((TreeTableColumn.CellDataFeatures<ProcessEntity, String> param) ->
                new ReadOnlyStringWrapper(param.getValue().getValue().getCpu() + "%"));
        ttc_memory.setCellValueFactory((TreeTableColumn.CellDataFeatures<ProcessEntity, String> param) -> {
            int m = param.getValue().getValue().getMemory();
            if (m / 1024 == 0) {
                return new ReadOnlyStringWrapper("0K");
            } else if (m / 1024 <= 1024) {
                return new ReadOnlyStringWrapper((m / 1024) + "K");
            } else
                return new ReadOnlyStringWrapper((m / 1024 / 1024) + "M");
        });

        ttc_path.setCellValueFactory((TreeTableColumn.CellDataFeatures<ProcessEntity, String> param) -> {
            return new ReadOnlyStringWrapper(param.getValue().getValue().getPath());
        });
        getAllProcess();
    }

    private List<ProcessEntity> getAllProcess() {
        Kernel32 kernel32 = Kernel32.INSTANCE;
        List<ProcessEntity> peList = new ArrayList<>(100);
        int hSnapShot = kernel32.CreateToolhelp32Snapshot(Kernel32.TH32CS_SNAPPROCESS, 0);
        ProcessEntry32 processEntry32 = new ProcessEntry32.ByReference();

        //获取第一个进程基本信息,传给processEntry32结构体
        int ret = kernel32.Process32First(hSnapShot, processEntry32);

        while (ret != 0) {
            ProcessEntity pe = new ProcessEntity(processEntry32.th32ProcessID, processEntry32.getSzExeFile(),
                    processEntry32.th32ParentProcessID, 0, 0, "");
            //获取进程路径
            if (processEntry32.th32ProcessID > 4) {
                int hss = kernel32.CreateToolhelp32Snapshot(Kernel32.TH32CS_SNAPmodule, processEntry32.th32ProcessID);
                ModuleEntry32 moduleEntry32 = new ModuleEntry32();
                if (kernel32.Module32First(hss, moduleEntry32) != 0) {
                    pe.setPath(moduleEntry32.getSzExePath());
                }
                kernel32.CloseHandle(hss);
            }
            TreeItem<ProcessEntity> treeItem = new TreeItem<>(pe);

            peList.add(pe);
            //获取下一个进程基本信息
            ret = kernel32.Process32Next(hSnapShot, processEntry32.free());
        }

        kernel32.CloseHandle(hSnapShot);
        Map<Integer, TreeItem<ProcessEntity>> pidMap = new HashMap<>(300);
        //使用ParentProcessID作为键的map
        Map<Integer, TreeItem<ProcessEntity>> parentIdMap = new HashMap<>(200);
        TreeItem<ProcessEntity> root = new TreeItem<>(peList.get(0));
        root.setExpanded(true);
        ttv_prc_list.setRoot(root);
        ttv_prc_list.setShowRoot(false);
        pidMap.put(0, root);
        //peList.sort(Comparator.naturalOrder());
        for (int i = 1; i < peList.size(); i++) {
            ProcessEntity pe = peList.get(i);
            System.out.println(pe);
            //先看看parentIdMap有没有自己的子项
            TreeItem<ProcessEntity> ti = parentIdMap.get(pe.getPid());
            if (ti != null) {
                ti.setValue(pe);
            } else {
                ti = new TreeItem<>(pe);
            }
            ti.setExpanded(true);
            ti.setGraphic(getPeIcon(pe));
            pidMap.put(pe.getPid(), ti);
            //接着看看有没有父级
            TreeItem<ProcessEntity> pTmp = pidMap.get(pe.getParentId());
            if (pTmp != null) { //如果父级已经被添加则把当前项添加到父级树枝下
                pTmp.getChildren().add(ti);
                parentIdMap.remove(pe.getPid());//删除已加入父级的节点
            } else {//否则创建一个父级把自己加入这个父级
                pTmp = parentIdMap.get(pe.getParentId());
                if (pTmp == null) {
                    pTmp = new TreeItem<>();
                    parentIdMap.put(pe.getParentId(), pTmp);
                }
                pTmp.getChildren().add(ti);
            }
        }
        //把剩余没有找到父进程的加入根部
        parentIdMap.forEach((k, v) -> {
            for (TreeItem<ProcessEntity> processEntityTreeItem : v.getChildren()) {
                root.getChildren().add(processEntityTreeItem);
            }
        });
        ttv_prc_list.refresh();
        return peList;
    }

    /**
     * 获取陪图标<br/>
     * 流程:<br/>
     * 获取缓存中的图标,如果没有往下;
     * 获取进程图标句柄>用gdi+转换成BitmapData>接着获取AARRGGBB字节拷贝到Java字节数组>再转换长Buff
     *
     * @param pe
     * @return
     */
    public static ImageView getProcessIcon(ProcessEntity pe)   {
        Image iv = exeIconsCache.get("");
        //Image iv = exeIconsCache.get(pe.getPath());
        if (iv != null){
            ImageView imageView = new ImageView(iv);
            imageView.setFitHeight(16);
            imageView.setFitWidth(16);
            return imageView;
        }
        ByteArrayInputStream bis=null;
        try {
            byte[] iconData = IconUtil.ExtractIcon(pe.getPath());
            if(iconData==null){
                ImageView imageView = new ImageView(exeIconsCache.get(""));
                imageView.setFitHeight(16);
                imageView.setFitWidth(16);
                return imageView;
            }
            bis = new ByteArrayInputStream(iconData);

            List<BufferedImage> images =ICODecoder.read(bis);

            //List<BufferedImage> images = IconParser.getInstance().getIcons(bis);

            int bestSize=0;
            BufferedImage result=null;
            //获取最佳尺寸
            for (int i = 0; i < images.size(); i++) {
                BufferedImage img= images.get(i);
                int abs=Math.abs(img.getWidth()-16)+Math.abs(img.getHeight()-16);
                System.out.println(img.getColorModel().getPixelSize());
                if(i==0){
                    bestSize= abs;
                    result=img;
                }
                if(abs==0){
                    break;
                }else if(abs<bestSize){
                    bestSize=abs;
                    result=img;
                }
            }
            iv=SwingFXUtils.toFXImage(result,null);
            exeIconsCache.put(pe.getPath(),iv);
        }catch (Exception e){
            //e.printStackTrace();
            iv=exeIconsCache.get("");
        }finally {
            try {
               if(bis!=null) bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        ImageView imageView = new ImageView(iv);
        imageView.setFitHeight(16);
        imageView.setFitWidth(16);
        return imageView;
    }

    /**
     * 结束一个进程
     * @param pe 进程对象
     */
    public boolean killProcess(ProcessEntity pe){
        Kernel32 kernel32 = Kernel32.INSTANCE;
        int hProc=kernel32.OpenProcess(Kernel32.PROCESS_TERMINATE,true,pe.getPid());
        boolean b = kernel32.TerminateProcess(hProc, 1);
        kernel32.CloseHandle(hProc);
        return b;
    }



    public static ImageView getPeIcon(ProcessEntity pe){
        //Image iv = exeIconsCache.get("");
        Image iv = exeIconsCache.get(pe.getPath());
        ImageView imageView = new ImageView();
        imageView.setFitHeight(16);
        imageView.setFitWidth(16);
        if (iv != null){
            imageView.setImage(iv);
            return imageView;
        }
        Shell32 shell32 = Shell32.INSTANCE;
        User32 user32=User32.INSTANCE;
        IconUtilDll icoDll=IconUtilDll.INSTANCE;
        //获取图标句柄
        IntByReference hIcon = new IntByReference(0);
        int i = shell32.ExtractIconExA(pe.getPath(), 0,null,hIcon , 1);
        byte[] data=new byte[32*32*4];
        IntByReference wp=new IntByReference(0);
        IntByReference hp=new IntByReference(0);
        int len = icoDll.GetIcon32ByteARGB(hIcon.getValue(),data.length, wp, hp, data);
        user32.DestroyIcon(hIcon.getValue());
        if(len==0){
            exeIconsCache.put(pe.getPath(),exeIconsCache.get(""));
            imageView.setImage(exeIconsCache.get(""));
            return imageView;
        }
        //System.out.println("len="+len+",wp="+wp.getValue()+",hp="+hp.getValue());
        //创建图像
        BufferedImage bufImg=new BufferedImage(wp.getValue(),hp.getValue(),BufferedImage.TYPE_4BYTE_ABGR);
        int p=0;
        for (int y=0;y<hp.getValue();y++){
            for (int x=0;x<wp.getValue();x++){
                int a=Byte.toUnsignedInt(data[p+3])<<24;
                int r=Byte.toUnsignedInt(data[p])<<16;
                int g=Byte.toUnsignedInt(data[p+1])<<8;
                int b=Byte.toUnsignedInt(data[p+2]);
                bufImg.setRGB(x,y,a|r|g|b);
                p+=4;
                //System.out.println(String.format("%08x",a|r|g|b));
            }
        }
        iv=SwingFXUtils.toFXImage(bufImg,null);
        exeIconsCache.put(pe.getPath(),iv);
        imageView.setImage(iv);
        return imageView;
    }




}
